# Copyright (c) 2019 Presto Labs Pte. Ltd.
# Author: jaewon

import datetime
import logging
import os

from coin.base.datetime_util import to_datetime
from coin.strategy.marketsim.core import spec_pb2
from experimental.prophet.craps.universe import universe_btc

from coin.strategy.marketsim import result_util
from coin.strategy.marketsim.strategy.pass_model_strategy import (PassModelSimStrategy,
                                                                  PassModelConfig,
                                                                  PassiveExecutorConfig,
                                                                  PassModelStrategyConfig)

run_id = None
sim_out_dir = None
symbol_nick = 'huobi-btcusd-quarter'
lt_model_version = 1


def prepare(args, ref_ts, **kwargs):
  global run_id, sim_out_dir, symbol_nick, lt_model_version
  run_id = kwargs.get('run_id', None)
  sim_out_dir = kwargs.get('sim_out_dir', None)
  if len(args) > 0:
    symbol_nick = args[0]
    logging.info('[sim] symbol=%s', symbol_nick)
  if len(args) > 1:
    lt_model_version = int(args[1])
    logging.info('[sim] lt_model_version=%s', str(lt_model_version))


def get_machine():
  return 'feed-01.ap-northeast-1.aws'


def get_time_ranges():
  ranges = []
  end_dt = datetime.datetime.now().date() - datetime.timedelta(days=2)
  cur_dt = end_dt - datetime.timedelta(days=30)
  while cur_dt < end_dt:
    if cur_dt.weekday() != 4:
      ranges.append((cur_dt, cur_dt + datetime.timedelta(hours=24)))
    cur_dt += datetime.timedelta(hours=24)

  logging.info('# days: %d', len(ranges))
  return ranges


def gen_sim_config(*,
                   symbol_nick,
                   lot_size,
                   stack,
                   sampler_offsets,
                   lt_model_version,
                   ref_ts,
                   machine):
  def second(x):
    return int(x * 10**9)

  universe = {s.nickname: s for s in universe_btc(ref_ts)}
  symbol_info = universe[symbol_nick]
  symbol_str = 'dummy'  # Must be specified in live

  sim_spec = spec_pb2.MarketSimSpecProto(
      latency=int(0.5 * 10**9),  # 1 sec,
      feed_cut_time_allowance=300 * 10**9,  # 5 min
      clear_on_feed_cut=True)

  product_sim_spec = spec_pb2.ProductSpecProto(symbol=str(symbol_info.product),
                                               settlement_currency='BTC',
                                               inverse=True,
                                               maker_fee=-0.00013,
                                               taker_fee=+0.0002,
                                               support_post_only=False,
                                               money_multiplier=100.,
                                               fastfeed_machine=machine,
                                               fastfeed_sub_req=symbol_info.sub_req)

  if not isinstance(sampler_offsets, dict):
    offset_bp = float(sampler_offsets)
    sampler_offsets = {
        'sampler/low_vol': offset_bp,
        'sampler/mid_vol': offset_bp,
        'sampler/high_vol': offset_bp,
    }

  model_filepath = 'model_%s.pkl.gz' % symbol_nick
  logging.info('Model filepath: %s', model_filepath)

  strategy_config = PassModelStrategyConfig(
      trade_product=symbol_info.product,
      trade_sub_req=symbol_info.sub_req,
      executor_params=PassiveExecutorConfig(
          symbol=symbol_str,
          lot_size=lot_size,
          min_pos=-lot_size * stack,
          max_pos=+lot_size * stack,
          order_update_period=second(2.),
          min_posting_period=second(0.2),
          max_posting_period=second(2.),
          fill_cooldown_period=second(0.01),
          post_only=False,
      ),
      model_config=PassModelConfig(
          machine=machine,  # Remove this in live.
          symbol_nick=symbol_nick,
          linear_model_filepath=model_filepath,
          bound_price=False,
          sampler_offsets=sampler_offsets,
          lt_model_param={'version': lt_model_version},
          ref_symbol_nicks=['bitflyer-fxbtcjpy'],
          ref_time=to_datetime(ref_ts),
      ),
  )

  return {
      'symbol_info': symbol_info,
      'sim_spec': sim_spec,
      'product_sim_spec': product_sim_spec,
      'strategy_config': strategy_config
  }


def get_strategy(from_ts, to_ts):
  machine = get_machine()

  strategy_list = []
  for offset_bp in (
      3.,
      5.,
  ):
    for stack in (5,):
      lot_size = 100. / stack  # 100 Contracts = USD 10000

      config = gen_sim_config(symbol_nick=symbol_nick,
                              lot_size=lot_size,
                              stack=stack,
                              sampler_offsets=offset_bp,
                              lt_model_version=lt_model_version,
                              ref_ts=from_ts,
                              machine=machine)
      name = 'pu_model.%s.offset%.0fbp.%02dstack.%s' % (
          config['symbol_info'].nickname, offset_bp, stack, to_datetime(from_ts).strftime('%Y%m%d'))

      strategy = PassModelSimStrategy(name=name,
                                      machine=machine,
                                      config=config['strategy_config'],
                                      trade_product_sim_spec=config['product_sim_spec'],
                                      sim_spec=config['sim_spec'])
      strategy_list.append(strategy)

  return strategy_list


def get_strategy_result(strategy):
  if run_id is not None and sim_out_dir is not None:
    out_dir = os.path.join(sim_out_dir, 'run-' + run_id)
    os.makedirs(out_dir, exist_ok=True)
    out_path = os.path.join(out_dir, strategy.name + '.gz')
    strategy.sim.dump_sim_result(out_path, tag=strategy.name)

  sim_stat = strategy.get_sim_stat()
  sim_stat.tag = strategy.name
  return sim_stat


def aggregate_result(results):
  result_util.aggregate_sim_stat(results, show_stat_only=False)
  # for res in results:
  #  print(res)
  # return result_util.aggregate_sim_result(results)
